package com.cqfae.deploycentral.app.services.impl;

import com.cqfae.deploycentral.api.utils.IdGenerator;
import com.cqfae.deploycentral.app.base.BaseConstantCode;
import com.cqfae.deploycentral.app.base.BaseConstants;
import com.cqfae.deploycentral.app.config.FileSyncInfo;
import com.cqfae.deploycentral.app.config.JenkinsInfo;
import com.cqfae.deploycentral.app.dao.entity.FileSync;
import com.cqfae.deploycentral.app.dao.entity.FileSyncUpdate;
import com.cqfae.deploycentral.app.dao.mapper.ConstantCodeMapper;
import com.cqfae.deploycentral.app.dao.mapper.FileSyncMapper;
import com.cqfae.deploycentral.app.services.FileSyncDetailService;
import com.cqfae.deploycentral.app.services.FileSyncService;
import com.cqfae.deploycentral.app.services.FileSyncUpdateService;
import com.cqfae.deploycentral.app.utils.ConvertUtil;
import com.cqfae.deploycentral.app.utils.GsonUtils;
import com.cqfae.deploycentral.app.utils.NullUtil;
import com.cqfae.deploycentral.app.web.request.FileSyncRequest;
import com.cqfae.deploycentral.app.web.response.IPage;
import com.cqfae.deploycentral.app.web.vo.FileSyncVo;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by JIANGLU009 on 2017/11/6.
 */
@Service
@Transactional
public class FileSyncServiceImpl implements FileSyncService {

    private static Logger logger = LoggerFactory.getLogger(FileSyncServiceImpl.class);

    // 获取ID
    @Autowired
    private IdGenerator idGenerator;

    @Autowired
    private FileSyncMapper fileSyncMapper;

    @Autowired
    private FileSyncInfo fileSyncInfo;

    @Autowired
    private ConstantCodeMapper constantCodeMapper;

    @Autowired
    private FileSyncDetailService fileSyncDetailService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private FileSyncUpdateService fileSyncUpdateService;

    @Autowired
    private JenkinsInfo jenkinsInfo;

    private String sqlAreaCode = "areaCode";

    private String sqlInteriorAreaCode = "interiorAreaCode";

    /**
     * 新增
     *
     * @param fileSync
     * @return
     */
    @Override
    public Integer add(FileSync fileSync) {
        Integer num = 0;
        if (fileSync != null) {
            // 对string类型的属性做trim
            trimStr(fileSync);
            // 验证编码
            codeCheck(fileSync);
            // 源文件地址
            String fileFromPath = fileSyncInfo.getUploadRootCachePath() + File.separator + fileSync.getName();
            // 清除上传的上传状态和同步状态
            fileSync.setUploadStatus(null);
            fileSync.setSyncStatus(null);
            // 文件移动
            fileMove(fileFromPath, fileSync);
            // 查询同目录下是否已上传相同名称文件夹
            Example example = new Example(FileSync.class);
            fileSyncCriteria(example, fileSync);
            // 查询
            List<FileSync> fileSyncList = fileSyncMapper.selectByExample(example);
            // 设置更新操作者和时间
            fileSync.setUpdateBy("123");
            fileSync.setUpdateTime(new Date());
            Long id = idGenerator.nextId();
            if (fileSyncList != null && !fileSyncList.isEmpty()) {
                num = fileSyncMapper.updateByExampleSelective(fileSync, example);
                fileSync.setId(fileSyncList.get(0).getId());
            } else {
                // 设置新增ID
                fileSync.setId(id);
                // 设置未同步
                fileSync.setSyncStatus(String.valueOf(BaseConstantCode.FILE_SYNC_TYPE_NO_CODE));
                // 设置新增操作者和时间
                fileSync.setCreateBy("123");
                fileSync.setCreateTime(new Date());
                num = fileSyncMapper.insert(fileSync);
            }

            // 本地上传备份
            fileSyncUpdateService.add(parseUpdate(fileSync));
        }
        return num;
    }


    /**
     * 文件上传
     *
     * @param file
     * @return
     * @throws IOException
     */
    @Override
    public FileSyncVo fileUpload(MultipartFile file) throws IOException {
        // 获得上传文件名字
        String originalFilename = file.getOriginalFilename();
        // 去掉空格文件名中的空格，避免因系统对空格不同识别符导致的错误
        originalFilename = originalFilename.replace(" ", "");
        // 获取上传文件类型
        String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf('.') + 1, originalFilename.length());

        // 上传到对应地址
        String fileRealPath = fileSyncInfo.getUploadRootCachePath() + File.separator + originalFilename;
        File destFile = new File(fileRealPath);
        logger.info("file copy to {}", fileRealPath);
        FileUtils.copyInputStreamToFile(file.getInputStream(), destFile);
        logger.info("file copy done");
        // 组装返回信息
        FileSyncVo fileSyncVo = new FileSyncVo();
        fileSyncVo.setTypeCode(String.valueOf(fileSuffix));
        fileSyncVo.setName(originalFilename);
        fileSyncVo.setFileId(originalFilename);
        fileSyncVo.setFileName(originalFilename);
        fileSyncVo.setFileType(String.valueOf(fileSuffix));

        return fileSyncVo;
    }

    /**
     * 删除文件
     *
     * @param fileId
     * @return
     */
    @Override
    public Integer fileDelete(String fileId) {
        String filePath = fileSyncInfo.getUploadRootCachePath() + File.separator + fileId;
        File file = new File(filePath);
        boolean deleteFlag = false;
        if (file.exists()) {
            deleteFlag = file.delete();
        }
        if (deleteFlag) {
            return 1;
        } else {
            return 0;
        }

    }

    /**
     * 更新
     *
     * @param fileSync
     * @return
     */
    @Override
    public Integer edit(FileSync fileSync) {
        Integer num = 0;
        if (fileSync != null && fileSync.getId() != null) {
            // 对string类型的属性做trim
            trimStr(fileSync);
            // 验证编码
            codeCheck(fileSync);
            // 清除上传的上传状态和同步状态、名称
            fileSync.setUploadStatus(null);
            fileSync.setSyncStatus(null);
            fileSync.setName(null);
            // 查询旧路径
            FileSyncRequest request = new FileSyncRequest();
            request.setId(fileSync.getId());
            FileSyncVo fileSyncVo = getOne(request);
            if (fileSyncVo != null) {
                // 将文件复制到新路径
                String fileFromPath = fileSyncInfo.getUploadRootPath()
                        + File.separator + fileSyncVo.getAreaCode()
                        + File.separator + fileSyncVo.getInteriorAreaCode()
                        + File.separator + fileSyncVo.getName();
                fileSync.setName(fileSyncVo.getName());
                fileMove(fileFromPath, fileSync);
                // 设置更新操作者和时间
                fileSync.setUpdateBy("123");
                fileSync.setUpdateTime(new Date());
                // 查询同目录下是否已上传相同名称文件夹
                Example example = new Example(FileSync.class);
                fileSyncCriteria(example, fileSync);
                // 查询
                List<FileSync> fileSyncList = fileSyncMapper.selectByExample(example);
                // 如果不是一条数据就删除
                if (fileSyncList != null && !fileSyncList.isEmpty() && fileSyncList.get(0).getId().longValue() != fileSync.getId().longValue()) {
                    // 删除冗余数据
                    fileSyncMapper.deleteByExample(example);
                }
                // 更新
                num = fileSyncMapper.updateByPrimaryKeySelective(fileSync);

                // 更新备份信息
                fileSyncUpdateService.edit(parseUpdate(fileSync));

            }
        }
        return num;

    }

    /**
     * 删除
     *
     * @param fileSync
     * @return
     */
    @Override
    public Integer delete(FileSync fileSync) {
        Integer num = 0;
        if (fileSync != null && fileSync.getId() != null) {
            FileSyncRequest fileSyncRequest = new FileSyncRequest();
            fileSyncRequest.setId(fileSync.getId());
            FileSyncVo fileSyncVo = getOne(fileSyncRequest);
            if (fileSyncVo != null) {
                // 删除文件
                String filePath = fileSyncInfo.getUploadRootPath()
                        + File.separator + fileSyncVo.getAreaCode()
                        + File.separator + fileSyncVo.getInteriorAreaCode()
                        + File.separator + fileSyncVo.getName();
                File file = new File(filePath);
                if (file.exists()) {
                    boolean result = file.delete();
                    logger.info("删除源文件结果：{}", result ? "成功" : "失败");
                }
                // 删除详情
                fileSyncDetailService.deleteByFileSyncId(String.valueOf(fileSync.getId()));
                // 删除
                num = fileSyncMapper.deleteByPrimaryKey(fileSync);
            }
        }
        return num;

    }

    /**
     * 翻页查询
     *
     * @param request
     * @return
     * @throws Exception
     */
    @Override
    public IPage<FileSyncVo> listByPage(FileSyncRequest request) {
        // 创建翻页对象
        Page<FileSyncVo> page = PageHelper.startPage(request.getPageNum(), request.getPageSize());
        // 组装查询条件
        Example example = new Example(FileSync.class);
        Example.Criteria criteria = example.createCriteria();
        fileSyncCriteria(criteria, request);
        // 按照更新时间倒叙
        example.orderBy("updateTime").desc();
        // 查询并设置数据
        fileSyncMapper.selectByExample(example);
        IPage<FileSyncVo> iPage = ConvertUtil.pageToIpage(page, new IPage<FileSyncVo>());
        // 转换VO
        List<FileSync> result = GsonUtils.getInstance().fromJson(GsonUtils.getInstance().toJson(page), new TypeToken<List<FileSync>>() {
        }.getType());
        List<FileSyncVo> resultVo = new ArrayList<>();
        for (FileSync fileSync : result) {
            FileSyncVo fileSyncVo = new FileSyncVo(fileSync);
            // 验证编码
            codeCheck(fileSyncVo);
            setCodeName(fileSyncVo);
            resultVo.add(fileSyncVo);
        }
        // 设置返回数据
        iPage.setResult(resultVo);
        return iPage;
    }

    /**
     * 根据ID查询一个实体
     *
     * @param request
     * @return
     */
    @Override
    public FileSyncVo getOne(FileSyncRequest request) {
        FileSyncVo vo = null;
        // 项目ID不能为空
        Assert.isTrue(request.getId() != null, "文件同步ID为空");
        FileSync fileSync = new FileSync();
        // ID和appName都是表唯一的
        fileSync.setId(request.getId());
        fileSync = fileSyncMapper.selectOne(fileSync);
        if (fileSync != null) {
            vo = new FileSyncVo(fileSync);
            // 验证编码
            codeCheck(vo);
            setCodeName(vo);
        }
        return vo;
    }

    /**
     * 文件同步
     *
     * @param request
     * @return
     */
    @Override
    public Integer fileSync(FileSyncRequest request) {
        // 设置更新条件
        Example example = new Example(FileSync.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo(sqlAreaCode, request.getAreaCode());
        criteria.andEqualTo(sqlInteriorAreaCode, request.getInteriorAreaCode());

        // 更新信息
        FileSync fileSync = new FileSync();
        fileSync.setSyncStatus(String.valueOf(BaseConstantCode.FILE_SYNC_TYPE_EXECUT_CODE));
        fileSync.setUpdateBy("123");
        fileSync.setUpdateTime(new Date());
        // 更新
        fileSyncMapper.updateByExampleSelective(fileSync, example);

        // 拼接访问地址token, area, zone , callbackUrl ,env ( dev, test ,prod )
        StringBuilder stringBuffer = new StringBuilder();
        stringBuffer.append(fileSyncInfo.getSyncPath());
        stringBuffer.append("?token=" + jenkinsInfo.getToken());
        stringBuffer.append("&area=" + request.getAreaCode());
        stringBuffer.append("&zone=" + request.getInteriorAreaCode());
        stringBuffer.append("&callbackUrl=" + fileSyncInfo.getCallBackHost() + fileSyncInfo.getCallBackMethod());
        stringBuffer.append("&env=" + fileSyncInfo.getEnv());
        String remoteUrl = stringBuffer.toString();
        logger.info("rest call url:{}", remoteUrl);
        ResponseEntity<String> responseEntity = restTemplate.exchange(remoteUrl,
                HttpMethod.GET, null, String.class);
        String responseEntityStr = GsonUtils.getInstance().toJson(responseEntity);
        logger.info("rest return result:{}", responseEntityStr);
        // 返回url调用结果码
        HttpStatus status = responseEntity.getStatusCode();
        return status.value();
    }

    /**
     * 验证编码
     *
     * @param object
     */
    private void codeCheck(Object object) {
        if (object instanceof FileSync) {
            FileSync fileSync = (FileSync) object;
            Assert.isTrue(NullUtil.isNotEmpty(BaseConstants.getConstantsCodeMap().get(fileSync.getAreaCode())),
                    "此区域类型编码不存在：" + fileSync.getAreaCode());
            Assert.isTrue(NullUtil.isNotEmpty(BaseConstants.getConstantsCodeMap().get(fileSync.getInteriorAreaCode())),
                    "此内网区域编码不存在：" + fileSync.getInteriorAreaCode());
        }
        if (object instanceof FileSyncVo) {
            FileSyncVo fileSyncVo = (FileSyncVo) object;
            Assert.isTrue(NullUtil.isNotEmpty(BaseConstants.getConstantsCodeMap().get(fileSyncVo.getAreaCode())),
                    "此区域类型编码不存在：" + fileSyncVo.getAreaCode());
            Assert.isTrue(NullUtil.isNotEmpty(BaseConstants.getConstantsCodeMap().get(fileSyncVo.getInteriorAreaCode())),
                    "此内网区域编码不存在：" + fileSyncVo.getInteriorAreaCode());
            Assert.isTrue(NullUtil.isNotEmpty(BaseConstants.getConstantsCodeMap().get(fileSyncVo.getUploadStatusCode())),
                    "此上传状态编码不存在：" + fileSyncVo.getUploadStatusCode());
            Assert.isTrue(NullUtil.isNotEmpty(BaseConstants.getConstantsCodeMap().get(fileSyncVo.getSyncStatusCode())),
                    "此同步状态编码不存在：" + fileSyncVo.getSyncStatusCode());
        }
    }

    /**
     * 设置code对应的name
     *
     * @param vo
     * @return
     */
    private void setCodeName(FileSyncVo vo) {
        vo.setAreaCodeStr(BaseConstants.getConstantsCodeMap().get(vo.getAreaCode()));
        vo.setInteriorAreaCodeStr(BaseConstants.getConstantsCodeMap().get(vo.getInteriorAreaCode()));
        vo.setTypeCodeStr(BaseConstants.getConstantsCodeMap().get(vo.getTypeCode()));
        vo.setUploadStatusCodeStr(BaseConstants.getConstantsCodeMap().get(vo.getUploadStatusCode()));
        vo.setSyncStatusCodeStr(BaseConstants.getConstantsCodeMap().get(vo.getSyncStatusCode()));
    }

    /**
     * 对string类型的属性做trim
     *
     * @param object
     * @return
     */
    private Object trimStr(Object object) {
        if (object != null) {
            Class<?> clz = object.getClass();
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                try {
                    if (null != field.get(object) && field.get(object) instanceof String) {
                        String val = String.valueOf(field.get(object));
                        val = val.trim();
                        field.set(object, val);
                    }
                } catch (Exception e) {
                    logger.error("FileSyncServiceImpl.trimStr IllegalAccessException", e);
                }
            }
        }
        return object;
    }

    /**
     * 组装查询条件
     *
     * @param criteria
     */
    private void fileSyncCriteria(Example.Criteria criteria, FileSyncRequest request) {
        // 名字模糊查询
        if (StringUtils.isNotBlank(request.getName())) {
            criteria.andLike("name", "%" + request.getName() + "%");
        }
        if (request.getBeginDate() > 0 || request.getEndDate() > 0) {
            Assert.isTrue(request.getEndDate() >= request.getBeginDate(), "日期不合法，请检查开始结束日期");
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
            Date startDate = null;
            Date endDate = null;
            try {
                startDate = df.parse(request.getBeginDate() + " 00:00:00");
                endDate = df.parse(request.getEndDate() + " 23:59:59");
            } catch (ParseException pe) {
                logger.warn("=======FileSyncServiceImpl.listByPage======{}", pe);
            }
            criteria.andBetween("createTime", startDate, endDate);
        }
        // 区域类型
        if (request.getAreaCode() != null) {
            criteria.andEqualTo(sqlAreaCode, request.getAreaCode());
        }
        // 内网区域
        if (request.getInteriorAreaCode() != null) {
            criteria.andEqualTo(sqlInteriorAreaCode, request.getInteriorAreaCode());
        }
        // 上传状态
        if (request.getUploadStatusCode() != null) {
            criteria.andEqualTo("uploadStatus", request.getUploadStatusCode());
        }
        // 同步状态
        if (request.getSyncStatusCode() != null) {
            criteria.andEqualTo("syncStatus", request.getSyncStatusCode());
        }
    }


    /**
     * 组装查询条件
     *
     * @param example
     * @param fileSync
     */
    private void fileSyncCriteria(Example example, FileSync fileSync) {
        Example.Criteria criteria = example.createCriteria();
        // 名字模糊查询
        criteria.andEqualTo("name", fileSync.getName());
        // 区域类型
        criteria.andEqualTo(sqlAreaCode, fileSync.getAreaCode());
        // 内网区域
        criteria.andEqualTo(sqlInteriorAreaCode, fileSync.getInteriorAreaCode());
    }

    /**
     * 文件移动
     *
     * @param fileFromPath
     * @param fileSync
     */
    private void fileMove(String fileFromPath, FileSync fileSync) {
        String fileToPath = fileSyncInfo.getUploadRootPath()
                + File.separator + fileSync.getAreaCode()
                + File.separator + fileSync.getInteriorAreaCode()
                + File.separator + fileSync.getName();
        File fileFrom = new File(fileFromPath);
        File fileTo = new File(fileToPath);
        // 源地址和目标地址相同时不作操作
        if (fileFrom.equals(fileTo)) {
            return;
        }
        if (!fileFrom.exists()) {
            Assert.isTrue(false, "上传文件不存在");
        }
        try {
            logger.info("file copy：\nfrom：{}\nto：{}", fileFromPath, fileToPath);
            FileUtils.copyFile(fileFrom, fileTo);
            logger.info("file copy done");
            fileSync.setUploadStatus(String.valueOf(BaseConstantCode.FILE_UPLOAD_TYPE_SUCCESS_CODE));
        } catch (IOException e) {
            fileSync.setUploadStatus(String.valueOf(BaseConstantCode.FILE_UPLOAD_TYPE_FAIL_CODE));
            logger.error("FileSyncServiceImpl add IOException : {}", e);
        } finally {
            // 删除源文件
            boolean result = fileFrom.delete();
            logger.info("{}文件移动完成，删除源文件", result);
        }
    }

    /**
     * @param fileSync
     * @return
     */
    private FileSyncUpdate parseUpdate(FileSync fileSync) {
        FileSyncUpdate fileSyncUpdate = new FileSyncUpdate();
        fileSyncUpdate.setFileSyncId(fileSync.getId());
        fileSyncUpdate.setAreaCode(fileSync.getAreaCode());
        fileSyncUpdate.setInteriorAreaCode(fileSync.getInteriorAreaCode());
        fileSyncUpdate.setName(fileSync.getName());
        fileSyncUpdate.setType(fileSync.getType());
        return fileSyncUpdate;
    }
}
